/*******************************************************************************
 Discription    :    功能说明:    1) 两个按键
 2) 支持 短按/长按/长长按/长长长按, 两个按键实现12个键值.
 性能说明:    1) 调用获取按键时参数决定是否要阻塞,阻塞会导致任务被挂起.
 2) 直接调用获取键值,不需要管其它任何东西.
 InputDepend    :    Timer interrupt tick(系统 或者 是硬件timer)
 Author        : 练达荣  Davin.Lien
 Version        : V1.0.0
 Date        : 2021/10/30
 History        :
 *******************************************************************************/

/*----------------------------must includes----------------------------------------*/
#include <rtthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <drivers/pin.h>

#include "board.h"
#include "key.h"

/*---------------------------- debug include ---------------------------------------*/

/*------------------------------移植相关代码_硬件部分---------------------------------*/
#define keyName_S2  29//"PA.4"
#define keyName_S3  30//"PA.5"
#define keyName_S4  31//"PA.6"

#define LevelWhenPress            0
/*------------------------------移植相关代码_软件部分---------------------------------*/
#define TimerWantIntCycle    20    // unit=ms:  固定扫描周期
#define TimerIntTick_ms        (1000/OS_TMR_CFG_TICKS_PER_SEC)    // unit=ms

#define debugNotice_Posi        opsDisp_TestResult
#define debugNotice_PosiData    opsDisp_Current

/*----------------------------- 移植无关码(按键功能需求有关) ----------------------------------------*/
#define KeyTimeLong_Realease    50
#define KeyTimeLong_Sms         50        // ShortKey按键时长
#define KeyTimeLong_Lms         1000    // L按键时长
#define KeyTimeLong_LLms        3000    // LL按键时长
#define KeyTimeLong_LLLms       5000    // LLL按键时长

#define Ev_Key  1
typedef struct
{
    rt_event_t event;
    rt_timer_t timer;

    uint8_t pins[8];
    TE_KeyCode keyCode;
    rt_uint16_t PressCnt;
    rt_uint16_t unPressCnt;
    rt_uint16_t cntKeyChange;

    uint8_t f_HaveNotice_S :1;
    uint8_t f_HaveNotice_L :1;
    uint8_t f_HaveNotice_LL :1;
    uint8_t f_HaveNotice_LLL :1;
} T_Ctl;
static T_Ctl ctl_key;

static void key_reset(void)
{
    ctl_key.PressCnt = 0;
    ctl_key.unPressCnt = 0;
}

static void key_NoticeFlagReset(void)
{
    ctl_key.f_HaveNotice_S = false;
    ctl_key.f_HaveNotice_L = false;
    ctl_key.f_HaveNotice_LL = false;
    ctl_key.f_HaveNotice_LLL = false;
}

static const char keyName[] = { keyName_S2, keyName_S3, keyName_S4 };
static void keyGPIOConfiguration(void)
{
    int pin;

    rt_kprintf("func:%s\n", __FUNCTION__);
    for (int i = 0; i < sizeof(keyName) / sizeof(keyName[0]); i++)
    {
//         pin = rt_pin_get(keyName[i]);
//         debug_StopAndInfo(-RT_ENOSYS == pin, "KeyInitErr");

//        rt_kprintf("io cfg:%d\n", i);
		pin = keyName[i];
        ctl_key.pins[i] = pin; 
        rt_pin_mode(pin, PIN_MODE_INPUT_PULLUP);
    }
}

static void callback(void *parg)
{
    static rt_uint8_t keyOld = 0;
    static int debug_cnt = 0;

    int keyCur = 0;                                                         // read keypad
    for (int i = sizeof(keyName) / sizeof(keyName[0]) - 1; i >= 0; i--)
    {
        keyCur <<= 1;
        keyCur |= (rt_pin_read(ctl_key.pins[i]) == LevelWhenPress);
    }

    if (keyCur != keyOld)                                                   // change to key code
    {
        if (keyCur == 0)
        {    // 按键释放
            if (++ctl_key.unPressCnt >= KeyTimeLong_Realease / TimerWantIntCycle
                    && (ctl_key.PressCnt > KeyTimeLong_Sms / TimerWantIntCycle))
            {                // 释放确认
//                rt_kprintf("%d\n", (1 << (sizeof(keyName) / sizeof(keyName[0]))));
                if (ctl_key.PressCnt > KeyTimeLong_LLLms / TimerWantIntCycle)
                {
                    keyCur = ((1 << (sizeof(keyName) / sizeof(keyName[0]))) - 1) * 3;
                }
                else if (ctl_key.PressCnt > KeyTimeLong_LLms / TimerWantIntCycle)
                {
                    keyCur = ((1 << (sizeof(keyName) / sizeof(keyName[0]))) - 1) * 2;
                }
                else if (ctl_key.PressCnt > KeyTimeLong_Lms / TimerWantIntCycle)
                {
                    keyCur = ((1 << (sizeof(keyName) / sizeof(keyName[0]))) - 1) * 1;
                }
                else if (ctl_key.PressCnt > KeyTimeLong_Sms / TimerWantIntCycle)
                {
                    keyCur = 0;
                }
                ctl_key.keyCode = keyCur + keyOld;
//                rt_kprintf("-----------------haveKey:%d keyCur:%d keyOld:%d\n", ctl_key.keyCode, keyCur, keyOld);

                key_reset();
                keyOld = 0;

                rt_event_send(ctl_key.event, (rt_uint32_t) Ev_Key);                      // send key
            }
            key_NoticeFlagReset();
        }
        else
        {                    // 按键抖动
            if (++ctl_key.cntKeyChange > 3)
            {
                keyOld = keyCur;
                key_reset();
                ctl_key.PressCnt = 1;
            }
        }
    }
    else
    {
        if (keyCur != 0)
        {
            ctl_key.cntKeyChange = 0;
            ctl_key.unPressCnt = 0;
            ++ctl_key.PressCnt;
            // ------------------ notice key time
            if (ctl_key.PressCnt > KeyTimeLong_Sms / TimerWantIntCycle && !ctl_key.f_HaveNotice_S)
            {
                ctl_key.f_HaveNotice_S = true;
                rt_kprintf("ShortKeyTime\n");       // 必要时这里换成播放 特定声音
            }
            else if (ctl_key.PressCnt > KeyTimeLong_Lms / TimerWantIntCycle && !ctl_key.f_HaveNotice_L)
            {
                ctl_key.f_HaveNotice_L = true;
                rt_kprintf("L KeyTime,Dong\n");       // 必要时这里换成播放 特定声音
            }
            else if (ctl_key.PressCnt > KeyTimeLong_LLms / TimerWantIntCycle && !ctl_key.f_HaveNotice_LL)
            {
                ctl_key.f_HaveNotice_LL = true;
                rt_kprintf("LL KeyTime,Dong-Dong\n");       // 必要时这里换成播放 特定声音
            }
            else if (ctl_key.PressCnt > KeyTimeLong_LLLms / TimerWantIntCycle && !ctl_key.f_HaveNotice_LLL)
            {
                ctl_key.f_HaveNotice_LLL = true;
                rt_kprintf("LLL KeyTime,Dong-Dong-Dong\n");       // 必要时这里换成播放 特定声音
            }
        }
    }
}

/*------------------------------------------------------- 全局函数 ------------------------------------------------------------*/
void key_Init(void)
{
    rt_err_t err;

    rt_memset(&ctl_key, 0, sizeof(ctl_key));
    keyGPIOConfiguration();

    ctl_key.timer = rt_timer_create("t_Time", callback, NULL, TimerWantIntCycle/10, RT_TIMER_FLAG_PERIODIC);
    debug_StopAndInfo(ctl_key.timer == NULL, "KeyTimeGetErr");

    ctl_key.event = rt_event_create("KeyEv", RT_IPC_FLAG_FIFO);
    debug_StopAndInfo(ctl_key.event == NULL, "KeyMBoxErr");

    debug_StopAndInfo(RT_EOK != rt_timer_start(ctl_key.timer), "KeyTimeStartErr");
}

TE_KeyCode key_GetCode(rt_uint32_t overT_ms)
{
    rt_uint32_t e;
    rt_err_t err = rt_event_recv(ctl_key.event, Ev_Key, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
            overT_ms == 0 ? RT_WAITING_FOREVER : overT_ms, &e);
    debug_StopAndInfo(err != RT_EOK && err != RT_ETIMEOUT, "KeyEventErr");
//    rt_kprintf("func:%s %d\n",__FUNCTION__, e);

    return ctl_key.keyCode;
}

void test_key(void)
{
    TE_KeyCode kc;
    rt_uint8_t debug;
    while (1)
    {
        kc = key_GetCode(0);

        rt_kprintf("KEY:%d press, time:%d\n", kc, debug++);
        rt_thread_mdelay(10);
    }
}

